feat(tools): export the llm session resume payload type and schema from the root [SAP-3184] - #821
feat(tools): export the llm session resume payload type and schema from the root [SAP-3184]#821gwitwer wants to merge 4 commits into
Conversation
…om the root The two llm deferred lanes were asymmetric at the package boundary: the async lane exported LlmRouteResultPayload + llmRouteResultSchema + LlmRouteResultSchemaError from `@sapiom/tools`, while a step paused on llm.createSession had nothing to type or validate its resumed input with, so authors hand-rolled both. Add LlmSessionReadyPayload (an LlmSession narrowed to the two terminal shapes the engine's resume forwarder delivers: `ready` with session-scoped baseUrls, or `failed` with the gateway's structured reason), plus llmSessionReadySchema and LlmSessionReadySchemaError, mirroring the route-result trio. Also re-export LlmSession, LlmSessionState, RoutingLabel, ModelLabel, readDisclosure and the disclosure types from the root — same class of gap, no behavior change. Purely additive; no existing export changes shape or name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W5yTA5mh9qL5P6e3oBc6GH
The provider-neutral copy guard audits packages/tools/src/llm/index.ts and flagged the new schema's bare `baseUrls.anthropic` / `baseUrls.openai` property accesses and error strings. Name the two wire-shape keys once as a const tuple and iterate it, and unmask exactly that tuple in the guard — the same narrow, one-usage-at-a-time approach the file's other wire-shape identifiers use. No behavior change: the schema still requires both base URLs on a ready session and produces the same error messages. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W5yTA5mh9qL5P6e3oBc6GH
Review — PR #821
|
…; trim root exports Review round 1: `llmSessionReadySchema` required `baseUrls` on a ready payload, which is stricter than anything in this package consumes — `callSession` needs only `sessionId`. Make it optional on the payload (as it is on `LlmSession`) and validate the pair only when present. The state union stays `ready | failed`: the signal fires once when the session leaves `pending`, and the engine folds any non-ready outcome into `failed` with the outcome as the reason, so no other state arrives on it. Also drop the raw wire-shape `LlmDisclosure` from the root barrel (it stays on the `llm` namespace / subpath); the camelCase `LlmDisclosureResult` and `readDisclosure` — which the ticket asks for — remain. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W5yTA5mh9qL5P6e3oBc6GH
|
Thanks — addressed in 14e6bb3 and the edited PR body. Confidentiality. Fixed. The PR body and AI-assistance note now use the same generic wording as the shipped copy ("the engine's resume forwarder") and name no internal files. Validator strictness. Partly agreed, partly declined:
Root exports. |
Follow-up review — PR #821Delta since Earlier findings — status
Still open
New
Nothing the earlier round got wrong: Verdict: Approve. Fix the |
…ayload contract Review round 2: the field doc still promised "present from READY on" while the session resume schema deliberately treats it as optional. Say when the gateway reports it and that callSession does not need it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W5yTA5mh9qL5P6e3oBc6GH
|
Thanks — addressed in 1083124.
|
Primary change type
Problem and motivation
The two
llmdeferred lanes are asymmetric at the@sapiom/toolsboundary. The async lane exports everything an author needs to type and validate a resumed step's input from the package root (LlmRouteResultPayload,llmRouteResultSchema,LlmRouteResultSchemaError). The session lane exports none of it: a step paused onllm.createSession(...)and resumed onLLM_SESSION_READY_SIGNALhas no root-exported type for its input and no validator, so anyone using it writes their own types and their own validation (or reaches for the./llmsubpath).Summary and scope
Additive only. From
packages/tools/src/llm/index.ts:LlmSessionReadyPayload— the input a step resumed frompauseUntilSignal(sessionHandle, …)receives. AnLlmSessionnarrowed to the two shapes the engine's resume forwarder delivers:state: "ready"(hand it tocallSession;baseUrlscarries the session-scoped URLs when reported), orstate: "failed"with a non-emptyerrorcarrying the gateway's structured reason (deadline_exhausted,grant_mint_failed,session_ready_failed,session_unsupported). The signal fires once, when the session leavespending, and the forwarder folds any non-ready outcome intofailed, so no other state arrives on it.llmSessionReadySchema/LlmSessionReadySchemaError— runtime validator + typed error, same shape of API asllmRouteResultSchema/LlmRouteResultSchemaError.baseUrlsis optional (as onLlmSession) and validated as a complete pair only when present.From
packages/tools/src/index.ts(the barrel), new root exports:LlmSessionReadyPayload,LlmSession,LlmSessionState(types),llmSessionReadySchema,LlmSessionReadySchemaErrorRoutingLabel,ModelLabel,LlmDisclosureResult(types) andreadDisclosure.scripts/provider-neutral-copy-check.mjsgains one narrow mask for the const tuple that names the two base-URL keys the schema validates.Out of scope: no server or gateway change; no change to any existing export's name or shape.
Related work
Related issue or discussion: SAP-3184 (part of epic SAP-3183).
Validation
Tests and documentation
llm/sessions.spec.ts: newllmSessionReadySchemablock — accepts a well-formed ready payload with and withoutbaseUrls(and the union narrows onstate), accepts a failed payload for each of the four gateway terminal reasons, rejects malformed payloads withLlmSessionReadySchemaError(non-object, missingsessionId, non-terminalstate, partial or non-objectbaseUrls, failed without a reason, mistyped optional fields).smoke.spec.ts: asserts both lanes' resume contracts (signal + payload type + schema + error) import from the root symmetrically, plusreadDisclosure,RoutingLabel,ModelLabel.LlmRouteResultPayload's and cross-links the two.Compatibility and release impact
@sapiom/tools: minor).Security
will follow the
Security Policy for
private reporting.
AI assistance
Written with Claude Code. It derived the payload shape from what the engine's resume forwarder delivers to a resumed step, wrote the schema, tests and changeset, and verified with the commands above.
Checklist
CONTRIBUTING.md, and this contribution follows the direct-PR or issue-first policy.any N/A checks above.
🤖 Generated with Claude Code
https://claude.ai/code/session_01W5yTA5mh9qL5P6e3oBc6GH